﻿using H.Nets;
using H.Utils.DataStruct;
using System;
using System.Collections.Concurrent;
using System.IO;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace H.Net.Nets
{
    /// <summary>
    /// TCPClient基本实现
    /// </summary>
    public abstract class AbstractTcpClient : ITcpClient
    {

        /// <summary>
        /// 客户端的标识符
        /// </summary>
        public abstract string Identifier { get; set; }

        /// <summary>
        /// 客户端是否能够使用
        /// </summary>
        public abstract bool IsValid { get; }

        /// <summary>
        /// 客户端是否已经连接
        /// </summary>
        public bool Connected => _Socket != null && _Socket.Connected;

        #region 事件
        /// <summary>
        /// 客户端连接状态发生改变事件
        /// </summary>
        public event ClientStatusHandler StatusChange;

        /// <summary>
        /// 客户端接收到消息事件
        /// </summary>
        public event DataRecieveHandler Recieved;

        /// <summary>
        /// 当客户端改名时,发射此事件
        /// </summary>
        public event ClientRenameHandler Rename;

        #endregion

        #region 内部字段
        /// <summary>
        /// 内部管理的套接字对象
        /// </summary>
        protected Socket _Socket;

        /// <summary>
        /// 内部网络流
        /// </summary>
        internal HFrameStream _FrameStream;

        /// <summary>
        /// 客户端实例的名称
        /// </summary>
        protected string _Identifier = null;


        /// <summary>
        /// 上一次通知外部的时候的状态
        /// </summary>
        private bool? _LastStatus = null;

        /// <summary>
        /// 内部管理的数据缓冲池
        /// </summary>
        private readonly CommonPool<BlockPointInfo> _BlockPool = new CommonPool<BlockPointInfo>();

        /// <summary>
        /// 存储所有当前被客户端阻塞的线程
        /// </summary>
        private readonly ConcurrentDictionary<int, BlockPointInfo> _BlockedThread = new ConcurrentDictionary<int, BlockPointInfo>();
        #endregion


        /// <summary>
        /// 初始化一个新的客户端
        /// </summary>
        /// <param name="socket">通过一个Socket初始化一个TcpClient</param>
        protected AbstractTcpClient(Socket socket)
        {
            _Socket = socket;
            if (_Socket != null)
            {
                _Socket.ReceiveBufferSize = 1 << 15;
            }
        }

        /// <summary>
        /// 客户端连接函数
        /// </summary>
        public abstract void Connect();

        /// <summary>
        /// <inheritdoc/>
        /// </summary>
        public void Disconnect()
        {
            if (_Socket == null || !_Socket.Connected)
            {
                return;
            }
            _Socket.Shutdown(SocketShutdown.Both);
            _Socket.Disconnect(false);
            NotifyChangeStatus(false);
        }


        ///<inheritdoc/>
        public abstract int CreateTopic();


        /// <summary>
        /// 开始读取消息
        /// </summary>
        protected Task StartRead(CancellationToken ct)
        {
            if (_Socket == null)
            {
                throw new Exception("Socket实例未创建");
            }

            return Task.Factory.StartNew(() =>
            {
                try
                {
                    while (!ct.IsCancellationRequested)
                    {
                        HWebFrame frame = _FrameStream.Read();
                        Console.WriteLine($"接收到一帧,\r\n 主题:{frame.TopicID}\r\n 类型:{frame.TypeID.ToString("x2")}\r\n 长度:{frame.DataLength}");
                        ProcessMessage(frame);
                    }
                }
                catch (EndOfStreamException)
                {
                    NotifyChangeStatus(false);
                }
                catch (Exception err)
                {
                    Console.WriteLine("读取数据发生错误" + err);
                    NotifyChangeStatus(false);
                }
            }, ct, TaskCreationOptions.LongRunning, TaskScheduler.Default);
        }



        /// <summary>
        /// 当客户端状态发生改变时,使用此函数发射事件
        /// </summary>
        protected void NotifyChangeStatus(bool status)
        {
            if (_LastStatus != status)
            {
                _LastStatus = status;
                StatusChange?.Invoke(status);
            }
        }

        /// <summary>
        /// 当客户端状态发生改变时,使用此函数发射事件
        /// </summary>
        /// <param name="oldName">旧名称</param>
        /// <param name="newName">新名称</param>
        protected void NotifyClientRename(string oldName, string newName)
        {
            Rename?.Invoke(oldName, newName);
        }

        ///<inheritdoc/>
        public bool Send(int topicID, int typeID)
        {
            if (_Socket == null || !_Socket.Connected)
            {
                NotifyChangeStatus(false);
                return false;
            }
            lock (_Socket)
            {
                try
                {
                    _FrameStream.Write(topicID, typeID);
                }
                catch (Exception err)
                {
                    NotifyChangeStatus(false);
                    Console.WriteLine(err);
                    return false;
                }
            }
            return true;
        }

        ///<inheritdoc/>
        public bool Send(int topicID, int typeID, int dataLength, byte[] buffer)
        {
            if (_Socket == null || !_Socket.Connected)
            {
                NotifyChangeStatus(false);
                return false;
            }
            lock (_Socket)
            {
                try
                {
                    _FrameStream.Write(topicID, typeID, dataLength, buffer);
                }
                catch (Exception err)
                {
                    NotifyChangeStatus(false);
                    Console.WriteLine(err);
                    return false;
                }
            }
            return true;
        }

        ///<inheritdoc/>
        public bool Send(int topicID, int typeID, string message)
        {
            if (_Socket == null || !_Socket.Connected)
            {
                NotifyChangeStatus(false);
                return false;
            }
            lock (_Socket)
            {
                try
                {
                    _FrameStream.Write(topicID, typeID, message);
                }
                catch (Exception err)
                {
                    NotifyChangeStatus(false);
                    Console.WriteLine(err);
                    return false;
                }
            }
            return true;
        }

        ///<inheritdoc/>
        public bool Send(int typeID, int dataLength, byte[] buffer)
        {
            return Send(CreateTopic(), typeID, dataLength, buffer);
        }

        ///<inheritdoc/>
        public bool Send(int typeID, string message)
        {
            return Send(CreateTopic(), typeID, message);
        }

        /// <summary>
        /// 统一处理消息
        /// </summary>
        /// <param name="frame"></param>
        private void ProcessMessage(HWebFrame frame)
        {
            switch (frame.TypeID)
            {
                case HFrameType.IDENTIFY:
                    //自动处理改名消息
                    Identifier = Encoding.UTF8.GetString(frame.Buffer, 0, frame.DataLength);
                    _FrameStream?.FreeFrame(frame);
                    break;
                case HFrameType.RBUFFERSIZE:
                    int rBufferSize = BitConverter.ToInt32(frame.Buffer, 0);
                    _Socket.ReceiveBufferSize = (rBufferSize > 0 ? rBufferSize : 0) + (1 << 15);
                    _FrameStream?.FreeFrame(frame);
                    break;
                default:
                    if (_BlockedThread.TryGetValue(frame.TopicID, out BlockPointInfo blocking))
                    {
                        //处理监听的主题
                        blocking.ReadFromBuffer.WaitOne();
                        if (blocking.Frame != null)
                        {
                            _FrameStream?.FreeFrame(blocking.Frame);
                        }

                        blocking.Frame = frame;
                        blocking.WroteInBuffer.Set();
                    }
                    else
                    {
                        //未监听的主题通过Recieve事件返回
                        if (Recieved == null)
                        {
                            _FrameStream?.FreeFrame(frame);
                        }
                        else
                        {
                            Recieved.BeginInvoke(frame.TopicID, frame.TypeID, frame.DataLength, frame.Buffer, iResult =>
                             {
                                 Recieved.EndInvoke(iResult);
                                 _FrameStream?.FreeFrame(frame);
                             }, null);
                        }
                    }
                    break;
            }
        }


        ///<inheritdoc/>
        public bool Exchange(int topicID, ref int typeID, ref int dataLength, ref byte[] buffer, int millisecondsTimeout = -1)
        {
            if (_BlockedThread.ContainsKey(topicID))
            {
                //话题已经被占用
                return false;
            }
            BlockPointInfo blocking = _BlockPool.Malloc();

            if (_BlockedThread.TryAdd(topicID, blocking))
            {
                try
                {
                    if (!Send(topicID, typeID, dataLength, buffer))
                    {
                        return false;
                    }

                    if (blocking.WroteInBuffer.WaitOne(millisecondsTimeout))
                    {
                        //将数据拷贝然后返回
                        HWebFrame frame = blocking.Frame;
                        typeID = frame.TypeID;
                        dataLength = frame.DataLength;
                        if (frame.DataLength > 0)
                        {
                            if (buffer.Length < dataLength)
                            {
                                //检查是否需要扩充缓冲区
                                buffer = new byte[dataLength];
                            }
                            Array.Copy(frame.Buffer, buffer, dataLength);
                        }
                        blocking.ReadFromBuffer.Set();
                        return true;
                    }
                    else
                    {
                        return false;
                    }
                }
                finally
                {
                    _BlockedThread.TryRemove(topicID, out blocking);
                    _BlockPool.Free(blocking);
                }
            }
            else
            {
                _BlockPool.Free(blocking);
                return false;
            }
        }

        ///<inheritdoc/>
        public bool Recieve(int topicID, out int typeID, out int dataLength, ref byte[] buffer, int millisecondsTimeout = -1)
        {
            if (_BlockedThread.ContainsKey(topicID))
            {
                //话题已经被占用
                typeID = 0;
                dataLength = 0;
                return false;
            }
            BlockPointInfo blocking = _BlockPool.Malloc();

            if (_BlockedThread.TryAdd(topicID, blocking))
            {
                try
                {
                    if (blocking.WroteInBuffer.WaitOne(millisecondsTimeout))
                    {
                        //将数据拷贝然后返回
                        HWebFrame frame = blocking.Frame;
                        typeID = frame.TypeID;
                        dataLength = frame.DataLength;
                        if (frame.DataLength > 0)
                        {
                            if (buffer.Length < dataLength)
                            {
                                //检查是否需要扩充缓冲区
                                buffer = new byte[dataLength];
                            }
                            Array.Copy(frame.Buffer, buffer, dataLength);
                        }
                        blocking.ReadFromBuffer.Set();
                        return true;
                    }
                    else
                    {
                        typeID = 0;
                        dataLength = 0;
                        return false;
                    }
                }
                finally
                {
                    _BlockedThread.TryRemove(topicID, out blocking);
                    _BlockPool.Free(blocking);
                }
            }
            else
            {
                typeID = 0;
                dataLength = 0;
                _BlockPool.Free(blocking);
                return false;
            }
        }

        ///<inheritdoc/>
        public virtual bool BeginBulkTransfer(int size)
        {
            return Send(HFrameType.RBUFFERSIZE, 4, BitConverter.GetBytes(size));
        }

        ///<inheritdoc/>
        public virtual bool EndBulkTransfer()
        {
            return BeginBulkTransfer(0);
        }

        #region 资源释放相关
        private bool disposed = false;

        private void Dispose(bool disposing)
        {
            if (disposing)
            {

            }
            if (!disposed)
            {
                Disconnect();
                disposed = true;
            }
        }

        public void Dispose()
        {
            Dispose(true);
        }



        ~AbstractTcpClient()
        {
            Dispose(false);
        }
        #endregion

    }
}
